// #pragma once
// #include <iostream>
// #include <string>
// #include <mutex>
// #include <assert.h>

// #include "oj_model.hpp"
// #include "oj_view.hpp"
// #include "../common/log.hpp"
// #include "../common/util.hpp"
// #include "../common/httplib.h"
// #include "jsoncpp/json/json.h"



// namespace ns_control
// {
//     using namespace ns_model;
//     using namespace ns_view;
//     using namespace ns_log;
//     using namespace ns_util;
//     using namespace httplib;

//     // 提供服务的主机
//     class Host
//     {
//     public:
//         std::string ip;  //编译服务的ip
//         int port;        //编译服务的port
//         uint64_t load;   //编译服务的负载 
//         // 可能存在多个客户端访问对应的主机或断开主机，需要同时修改load,所以需要加锁保护
//         std::mutex *mtx; // mutex禁止拷贝的，使用指针
//     public:
//         Host() : ip(""), port(0), load(0),mtx(nullptr)
//         {}
//         ~Host()
//         {}
//     public:
//         // 提升主机负载
//         void IncLoad()
//         {
//             if (mtx) mtx->lock();
//             ++load;
//             if (mtx) mtx->unlock();
//         }
//         // 减少主机负载
//         void DecLoad()
//         {
//             if (mtx) mtx->lock();
//             --load;
//             if (mtx) mtx->unlock();
//         }
//         // 重置负载
//         void ResetLoad()
//         {
//             if(mtx) mtx->lock();
//             load = 0;
//             if(mtx) mtx->unlock();
//         }
//         // 获取主机负载(没有太大的意义，只是为了统一接口)
//         uint64_t Load()
//         {
//             uint64_t _load = 0;
//             if (mtx) mtx->lock();
//             _load = load;
//             if (mtx) mtx->unlock();

//             return _load;
//         }
    
//     };

//     const std::string server_host = "./conf/server_host.conf";
//     // 负载均衡
//     class LoadBlance
//     {
//     private:
//         // 可以给我们提供编译服务的所有的主机
//         // 每一台主机都有自己的下标，充当当前主机的id
//         std::vector<Host> hosts;
//         // 所有在线的主机id
//         std::vector<int> online;
//         // 所有离线的主机id
//         std::vector<int> offline;
//         // 保证LoadBlance它的数据安全
//         std::mutex mtx;
//     public:
//         LoadBlance()
//         {
//             assert(LoadConf(server_host));
//             LOG(INFO) << "加载 " << server_host << " 成功" << "\n";
//         }
//         ~LoadBlance()
//         {
//         }
//     public:
//         // 加载所有可用主机
//         bool LoadConf(const std::string &server_host)
//         {
//             // 打开主机配置文件
//             std::ifstream in(server_host); 
//             if (!in.is_open())
//             {
//                 LOG(FATAL) << " 加载: " << server_host << " 失败" << "\n";
//                 return false;
//             }
//             // 读取配置文件
//             std::string line;
//             while (std::getline(in, line))
//             {
//                 std::vector<std::string> splits;
//                 // line : 127.0.0.1 8080 切分
//                 StringUtil::SplitString(line, &splits, ":");
//                 if (splits.size() != 2)
//                 {
//                     LOG(WARNING) << " 切分 " << line << " 失败" << "\n";
//                     continue;
//                 }
//                 // 填写主机属性信息
//                 Host m;
//                 m.ip = splits[0];
//                 m.port = atoi(splits[1].c_str());
//                 m.load = 0;
//                 m.mtx = new std::mutex();

//                 online.push_back(hosts.size()); 
//                 hosts.push_back(m);
//             }

//             in.close();
//             return true;            
//         }
//         // 智能选择一台可用主机,存在多个客户端同时访问oj服务，都需要选择主机，需要加锁
//         bool SmartChoice(int *id, Host **m)
//         {
//             mtx.lock();
//             // 负载均衡的算法
//             // 1. 随机数+hash
//             // 2. 轮询+hash
//             int online_num = online.size();
//             if (online_num == 0)
//             {
//                 mtx.unlock();
//                 LOG(FATAL) << " 所有的后端编译主机已经离线, 请运维的尽快查看" << "\n";
//                 return false;
//             }
//             // 通过遍历的方式，找到所有负载最小的机器
//             *id = online[0];
//             *m = &hosts[online[0]];
//             uint64_t min_load = hosts[online[0]].Load();
//             for (int i = 1; i < online_num; i++)
//             {
//                 uint64_t curr_load = hosts[online[i]].Load();
//                 if (min_load > curr_load)
//                 {
//                     min_load = curr_load;
//                     *id = online[i];
//                     *m = &hosts[online[i]];
//                 }
//             }
//             mtx.unlock();
//             return true;
//         }
//         // 将某台主机下线, 存在多个服务器退出情况，需要加锁
//         void OfflineMachine(int which)
//         {
//             mtx.lock();
//             for(auto iter = online.begin(); iter != online.end(); iter++)
//             {
//                 if(*iter == which)
//                 {
//                     hosts[which].ResetLoad();
//                     //要离线的主机已经找到啦
//                     online.erase(iter);
//                     offline.push_back(which);
//                     break; //因为break的存在，所有我们暂时不考虑迭代器失效的问题
//                 }
//             }
//             mtx.unlock();
//         }
//         // 将某台主机上线
//         void OnlineMachine()
//         {
//             //我们统一上线
//             mtx.lock();
//             online.insert(online.end(), offline.begin(), offline.end());
//             offline.erase(offline.begin(), offline.end());
//             mtx.unlock();

//             LOG(INFO) << "所有的主机有上线啦!" << "\n";        
//         }
//         // 测试打印上线和下线机器编号代码
//         void ShowMachines()
//         {
//             mtx.lock();
//             std::cout << "当前在线主机列表: ";
//             for(auto &id : online)
//             {
//                 std::cout << id << " ";
//             }
//             std::cout << std::endl;
//             std::cout << "当前离线主机列表: ";
//             for(auto &id : offline)
//             {
//                 std::cout << id << " ";
//             }
//             std::cout << std::endl;
//             mtx.unlock();
//         }

//     };
//     class Control
//     {
//     private:
//         Model model_; //提供后台数据
//         View view_;   //提供html渲染功能
//         LoadBlance load_blance_; //核心负载均衡器
//     public:
//         Control()
//         {}
//         ~Control()
//         {}
//     public:
//         void RecoveryHost()
//         {
//             load_blance_.OnlineMachine();
//         }
//         //根据题目数据构建网页
//         // html: 输出型参数
//         bool AllQuestions(std::string *html)
//         {
//             bool ret = true;
//             std::vector<struct Question> all;
//             if (model_.GetAllQuestions(&all))
//             {
//                 std::sort(all.begin(), all.end(), [](const struct Question &q1, const struct Question &q2){
//                     return atoi(q1.number.c_str()) < atoi(q2.number.c_str());
//                 });
//                 // 获取题目信息成功，将所有的题目数据构建成网页
//                 view_.AllExpandHtml(all, html);
//                 // std::cout << "oj_control::AllQuestions()" << std::endl;

//             }
//             else
//             {
//                 *html = "获取题目失败, 形成题目列表失败";
//                 ret = false;
//             }
//             return ret;
//         }
//         bool Question(const std::string &number, std::string *html)
//         {
//             bool ret = true;
//             struct Question q;
//             if (model_.GetOneQuestion(number, &q))
//             {
//                 // 获取指定题目信息成功，将所有的题目数据构建成网页
//                 view_.OneExpandHtml(q, html);
//                 // std::cout << html << std::endl;
//             }
//             else
//             {
//                 *html = "指定题目: " + number + " 不存在!";
//                 ret = false;
//             }
//             return ret;
//         }
//         //
//         void Judge(const std::string &number, const std::string in_json, std::string *out_json)
//         {
//             // 0. 根据题目编号，直接拿到对应的题目细节
//             struct Question q;
//             model_.GetOneQuestion(number, &q);

//             // 1. in_json进行反序列化，得到题目的id，得到用户提交源代码，input
//             Json::Reader reader;
//             Json::Value in_value;
//             reader.parse(in_json, in_value);
//             std::string code = in_value["code"].asString();

//             // 2. 重新拼接用户代码+测试用例代码，形成新的代码
//             Json::Value compile_value;
//             compile_value["input"] = in_value["input"].asString();
//             compile_value["code"] = code + "\n" + q.tail;
//             compile_value["cpu_limit"] = q.cpu_limit;
//             compile_value["mem_limit"] = q.mem_limit;
//             Json::FastWriter writer;
//             std::string compile_string = writer.write(compile_value);

//             // 3. 选择负载最低的主机
//             // 规则: 一直选择，直到主机可用，否则，就是全部挂掉
//             while(true)
//             {
//                 int id = 0;
//                 Host *m = nullptr;
//                 if(!load_blance_.SmartChoice(&id, &m))
//                 {
//                     break;
//                 }

//                 // 4. 然后发起http请求，得到结果
//                 Client cli(m->ip, m->port);
//                 m->IncLoad();
//                 LOG(INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << m->ip << ":" << m->port << " 当前主机的负载是: " << m->Load() << "\n";
//                 // 这里自己犯了一个错，配置文件8080端口已经被占用，在申请res为1，不断死循环
//                 auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8");
//                 // std:: cout << res << res->status << std::endl;

//                 if(res)
//                 {
//                     // 5. 将结果赋值给out_json
//                     if(res->status == 200)
//                     {
//                         *out_json = res->body;
//                         m->DecLoad();
//                         LOG(INFO) << "请求编译和运行服务成功..." << "\n";
//                         break;
//                     }
//                     m->DecLoad();
//                 }
//                 else
//                 {
//                     //请求失败
//                     LOG(ERROR) << " 当前请求的主机id: " << id << " 详情: " << m->ip << ":" << m->port << " 可能已经离线"<< "\n";
//                     load_blance_.OfflineMachine(id);
//                     load_blance_.ShowMachines(); //是为了用来调试
//                 }
//             }
//         } 
//     };
// }